//
// Copyright (c) 2009 All Right Reserved
//
// vl
//
// 2009-01-01
// Contains ...
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics.Contracts;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Xml.Linq;
using System.Xml.Serialization;
using JetBrains.Annotations;
using LargoCommon.Abstract;
using LargoCommon.Interfaces;
namespace LargoCommon.Music
{
/// Rhythmical structure.
/// Rhythmical structure represents rhythm of one bar. It is always assigned to certain
/// rhythmical modality (and to rhythmical system). It has some inner characteristics
/// (mobility, tension, entropy,..).
[Serializable]
[XmlRoot]
public sealed class RhythmicStructure : FiguralSchema, IRhythmic, IModalStruct
{
#region Constructors
/// Initializes a new instance of the RhythmicStructure class. Serializable.
public RhythmicStructure() {
}
///
/// Initializes a new instance of the RhythmicStructure class.
///
/// The given system.
/// Structural code.
public RhythmicStructure(GeneralSystem givenSystem, string structuralCode)
: base(givenSystem, structuralCode) {
Contract.Requires(givenSystem != null);
this.DetermineToneLevel();
}
///
/// Initializes a new instance of the RhythmicStructure class.
///
/// The given system.
/// Number of instance.
public RhythmicStructure(GeneralSystem givenSystem, decimal number)
: base(givenSystem, number) {
Contract.Requires(givenSystem != null);
this.DetermineToneLevel();
}
/// Initializes a new instance of the RhythmicStructure class.
/// Rhythmical structure.
public RhythmicStructure(FiguralStructure structure)
: base(structure) {
Contract.Requires(structure != null);
this.DetermineToneLevel();
}
///
/// Initializes a new instance of the RhythmicStructure class.
///
/// System order.
/// Binary Shape.
public RhythmicStructure(byte sysOrder, RhythmicShape shape) ////
: base(RhythmicSystem.GetRhythmicSystem(RhythmicDegree.Structure, sysOrder), null) {
Contract.Requires(shape != null);
if (shape == null || this.GSystem.Order == 0) {
return;
}
this.SetElement(0, 2); //// start of pause needed!
for (byte e = 0; e < this.GSystem.Order; e++) {
if (shape.IsOn(e)) {
this.SetElement(e, (byte)RhythmicElement.StartTone);
}
}
//// 2016/05
this.CompleteFromElements();
this.DetermineBehavior();
}
///
/// Initializes a new instance of the RhythmicStructure class. Serializable.
///
/// Rhythmical order.
/// Musical tones.
public RhythmicStructure(byte rhythmicOrder, IList musicalTones)
: base(RhythmicSystem.GetRhythmicSystem(RhythmicDegree.Structure, rhythmicOrder), null) {
Contract.Requires(musicalTones != null);
//// if (musicalTones == null) { return; }
var cnt = musicalTones.Count;
this.SetElement(0, (byte)RhythmicElement.StartRest); //// Potential pause start
for (var i = 0; i < cnt; i++) {
var mt = musicalTones.ElementAt(i);
if (mt == null) {
continue;
}
//// Tone start
this.SetElement(mt.BitFrom, mt.IsPause ? (byte)RhythmicElement.StartRest : (byte)RhythmicElement.StartTone);
//// Potential pause start
var limitTo = rhythmicOrder;
if (i + 1 < cnt) {
var nextTone = musicalTones.ElementAt(i + 1);
if (nextTone != null) {
limitTo = nextTone.BitFrom;
}
}
if (mt.BitRange.BitTo + 1 < limitTo) { //// (mt.BarNumberFrom)
this.SetElement((byte)(mt.BitRange.BitTo + 1), (byte)RhythmicElement.StartRest); //// (mt.BarNumberFrom)
}
}
//// 2015/01 Added condition && !firstTone.IsPause
if (musicalTones.FirstOrDefault() is MusicalStrike firstTone && firstTone.BitFrom == 0
&& !firstTone.IsPause && firstTone.IsFromPreviousBar) {
this.SetElement(0, (byte)RhythmicElement.ContinuePrevious);
}
//// this.DetermineINumber();
this.DetermineLevel();
this.DetermineToneLevel();
//// this.ResetSchema();
//// this.ComputeRhythmicProperties();
}
/// Initializes a new instance of the RhythmicStructure class.
/// The given system.
/// The mark rhythm.
public RhythmicStructure(GeneralSystem givenSystem, XElement markRhythm)
: base(givenSystem, string.Empty) {
string code = XmlSupport.ReadStringAttribute(markRhythm.Attribute("Code"));
this.SetStructuralCode(code);
}
#endregion
#region Properties - Xml
/// Gets Xml representation.
/// Property description.
public XElement GetXElement {
get {
var xe = new XElement(
"Structure",
new XAttribute("Code", this.GetStructuralCode),
new XAttribute("Schema", this.ElementSchema));
return xe;
}
}
#endregion
#region Interface - simple properties
/// Gets or sets Tone Level.
/// Property description.
public byte ToneLevel { get; set; }
///
/// Gets a value indicating whether is from previous bar.
///
/// The is from previous bar.
public bool IsFromPreviousBar => this.ElementList.ElementAt(0) == (byte)RhythmicElement.ContinuePrevious;
///
/// Gets a value indicating whether this instance has properties.
///
///
/// Is true if this instance has properties; otherwise, false.
///
public bool HasProperties => this.Properties.ContainsKey(GenProperty.FormalMobility);
///
/// Gets or sets a value indicating whether this instance is used.
///
///
/// true if this instance is used; otherwise, false.
///
public bool IsUsed { get; set; }
///
/// Gets the element schema and occurrence.
///
/// Property description.
[UsedImplicitly]
public string ElementSchemaAndOccurrence => string.Format(
CultureInfo.CurrentCulture,
"({0}/{1}) {2} ({3})",
this.ToneLevel,
this.Level,
this.ElementSchema,
this.Occurrence);
///
/// Gets a value indicating whether [start with formal rest].
///
///
/// True if [start with formal rest]; otherwise, false.
///
public bool StartsWithFormalRest {
get {
if (this.ElementList.Count == 0) {
return true;
}
var first = (byte)this.ElementList[0];
if (first != 2) {
return false;
}
byte length = 1;
for (byte i = 1; i < this.ElementList.Count; i++) {
var value = (byte)this.ElementList[i];
length++;
if (value != (byte)RhythmicElement.ContinuePrevious) {
break;
}
}
return length >= this.RhythmicSystem.Order / 3;
}
}
///
/// Gets a value indicating whether [end with formal rest].
///
///
/// true if [end with formal rest]; otherwise, false.
///
public bool EndsWithFormalRest {
get {
byte length = 0;
for (var i = this.ElementList.Count - 1; i >= 0; i--) {
var value = (byte)this.ElementList[i];
if (value == (byte)RhythmicElement.StartTone) {
return false;
}
length++;
if (value == (byte)RhythmicElement.StartRest) {
break;
}
}
return length >= this.RhythmicSystem.Order / 3;
}
}
#endregion
#region Interface - object properties
/// Gets rhythmical system.
/// Property description.
[XmlIgnore]
public RhythmicSystem RhythmicSystem => (RhythmicSystem)this.GSystem;
/// Gets or sets rhythmical modality.
/// Property description.
[XmlIgnore]
public RhythmicModality RhythmicModality {
get => (RhythmicModality)this.Modality;
set => this.Modality = value;
}
///
/// Gets the get rhythmic shape.
///
///
/// The get rhythmic shape.
///
public RhythmicShape GetRhythmicShape {
get {
var rsystem = RhythmicSystem.GetRhythmicSystem(RhythmicDegree.Shape, this.Order);
var bitArray = new BitArray(this.Order);
for (byte i = 0; i < this.Order; i++) {
bitArray[i] = this.IsOn(i);
}
var shape = new RhythmicShape(rsystem, bitArray);
return shape;
}
}
/// Gets or sets previous rhythmical structure.
/// Property description.
[XmlIgnore]
public RhythmicStructure PreviousStruct { get; set; }
///
/// Gets the code mark.
///
///
/// The code mark.
///
public string CodeMark {
get {
int beat = (int)this.RhythmicBehavior.Beat / 4; //// ?!?!? bounds of Variance??? RhythmicBehavior.Tension
var beatCode = MusicalProperties.GetLetter(beat, true);
int tension = (int)this.FormalBehavior.Variance / 10; //// ?!?!? bounds of Variance??? RhythmicBehavior.Tension
var tensionCode = MusicalProperties.GetLetter(tension, true);
var codeMark = string.Format(
"{0,2}/{1,2}-{2}.{3}",
this.Level,
this.Level - this.ToneLevel,
tensionCode,
beatCode);
return codeMark;
}
}
#endregion
#region Static factory methods
///
/// Get new harmonic structure.
///
/// The given system.
/// Structural number.
///
/// Returns value.
///
public static RhythmicStructure GetNewRhythmicStructure(GeneralSystem givenSystem, long number) {
Contract.Requires(givenSystem != null);
Contract.Ensures(Contract.Result() != null);
var rs = new RhythmicStructure(givenSystem, number);
rs.DetermineBehavior();
return rs;
}
///
/// Gets Rhythmic Structure.
///
/// The given system.
/// Structural code.
///
/// Returns value.
///
public static RhythmicStructure GetNewRhythmicStructure(GeneralSystem givenSystem, string structuralCode) {
Contract.Requires(givenSystem != null);
Contract.Ensures(Contract.Result() != null);
var rs = new RhythmicStructure(givenSystem, structuralCode);
rs.DetermineBehavior();
return rs;
}
///
/// Gets the full structure.
///
/// The rhythmic order.
/// Returns value.
public static RhythmicStructure GetFullStructure(byte rhythmicOrder) {
var rsystem = RhythmicSystem.GetRhythmicSystem(RhythmicDegree.Structure, rhythmicOrder);
var code = $"1,{rhythmicOrder - 1}*0";
var rstruct = GetNewRhythmicStructure(rsystem, code);
return rstruct;
}
///
/// Gets the rest structure.
///
/// The rhythmic order.
/// Returns value.
public static RhythmicStructure GetRestStructure(byte rhythmicOrder) {
var rsystem = RhythmicSystem.GetRhythmicSystem(RhythmicDegree.Structure, rhythmicOrder);
var code = $"2,{rhythmicOrder - 1}*0";
var rstruct = GetNewRhythmicStructure(rsystem, code);
return rstruct;
}
#endregion
#region Public methods
/// Makes a deep copy of the RhythmicStructure object.
/// Returns object.
public override object Clone() {
return GetNewRhythmicStructure(this.GSystem, this.GetStructuralCode);
}
///
/// Range of bit pair on given Level.
///
///
/// Returns value.
///
public BitRange OverrunRange() {
if (this.IsOn(0)) { //// this.Level == 0 ||
return null;
}
var order = this.GSystem.Order;
byte place = 0; //// this.PlaceAtLevel(0);
for (byte tick = 0; tick < order; tick++) {
if (tick > this.ElementList.Count - 1) { //// 2014/01
break;
}
if (this.ElementList[tick] <= 0) {
continue;
}
place = tick;
break;
}
if (place == 0) {
place = order;
}
var range = new BitRange(order, 0, place);
return range;
}
///
/// Half-divided structure.
///
///
/// Returns value.
///
public RhythmicStructure HalfDividedStructure() {
var rs = new RhythmicStructure { GSystem = this.GSystem };
byte cnt = 0;
short lastToneElement = -1;
byte lastValue = 0;
for (byte i = 0; i < this.ElementList.Count; i++) {
var value = (byte)this.ElementList[i];
if (value > 0) {
if (value == 1) {
if (lastToneElement >= 0) {
AddPartialElements(rs, cnt, lastValue);
}
lastValue = value;
}
lastToneElement = i;
cnt = 0;
}
cnt++;
}
if (lastToneElement >= 0) {
AddPartialElements(rs, cnt, lastValue);
}
rs.CompleteFromElements();
rs.DetermineBehavior();
return rs;
}
///
/// Inverted structure.
///
/// Returns value.
public new RhythmicStructure InvertedStructure() {
var rs = new RhythmicStructure { GSystem = this.GSystem };
byte cnt = 0;
for (byte i = 0; i < this.ElementList.Count; i++) {
var value = (byte)this.ElementList[this.ElementList.Count - i - 1];
if (value > 0) {
rs.ElementList.Add(value);
for (byte n = 0; n < cnt; n++) {
rs.ElementList.Add(0);
}
cnt = 0;
}
else {
cnt++;
}
}
rs.CompleteFromElements();
rs.DetermineBehavior();
return rs;
}
/// Validity test.
/// Returns value.
public override bool IsEmptyStruct() {
return this.IsOff(0) || base.IsEmptyStruct();
}
/// Validity test.
/// Returns value.
public override bool IsValidStruct() {
var ok = true;
byte lastNum = 0;
for (byte e = 0; e < this.GSystem.Order; e++) {
if (e >= this.ElementList.Count) {
continue;
}
var num = (byte)this.ElementList[e];
if (num == (byte)RhythmicElement.StartRest && lastNum == (byte)RhythmicElement.StartRest) { // two pauses in sequence
ok = false;
break;
}
if (num != 0) {
lastNum = num;
}
}
return ok;
}
/// Evaluate properties of the structure.
public override void DetermineBehavior() {
//// this.SetElements();
this.DetermineToneLevel();
this.ComputeRhythmicProperties();
this.ComputeMobility();
}
///
/// Converts to system.
///
/// The given system.
///
/// Returns value.
///
public RhythmicStructure ConvertToSystem(GeneralSystem givenSystem) { //// RhythmicSystem
Contract.Requires(givenSystem != null);
var realRhythmicOrder = givenSystem.Order;
var d = (float)realRhythmicOrder / this.Order;
var realStruct = new RhythmicStructure(givenSystem, string.Empty);
float index = 0;
for (byte bit = 0; index < realRhythmicOrder; index += d, bit++) {
var element = (byte)Math.Round(index);
if (bit < this.ElementList.Count && element < realStruct.ElementList.Count) {
realStruct.ElementList[element] = this.ElementList[bit];
}
}
//// 2016/05
realStruct.CompleteFromElements();
realStruct.DetermineBehavior();
return realStruct;
}
///
/// Determine ToneLevel.
///
public void DetermineToneLevel() {
byte s = 0;
for (byte e = 0; e < this.GSystem.Order; e++) {
if (e < this.ElementList.Count && this.ElementList[e] == 1) {
s++;
}
}
this.ToneLevel = s;
if (!this.Properties.ContainsKey(GenProperty.ToneLevel)) {
this.Properties[GenProperty.ToneLevel] = this.ToneLevel;
}
}
#endregion
#region Comparison
/// Support sorting according to level and ElementSchema.
/// Object to be compared.
/// Returns value.
public override int CompareTo(object obj) {
if (!(obj is RhythmicStructure rs)) {
return 0;
}
if (this.Level < rs.Level) {
return -1;
}
return this.Level > rs.Level ? 1 : string.Compare(this.ElementSchema, rs.ElementSchema, StringComparison.Ordinal);
//// This kills the DataGrid
//// throw new ArgumentException("Object is not a RhythmicStructure");
}
/// Test of equality.
/// Object to be compared.
/// Returns value.
public override bool Equals(object obj) {
//// check null (this pointer is never null in C# methods)
if (object.ReferenceEquals(obj, null)) {
return false;
}
if (object.ReferenceEquals(this, obj)) {
return true;
}
if (this.GetType() != obj.GetType()) {
return false;
}
return this.CompareTo(obj) == 0;
}
/// Support of comparison.
/// Returns value.
public override int GetHashCode() {
return this.ElementSchema.GetHashCode();
}
#endregion
#region String representation
/// List of figure elements.
/// Returns value.
public override string ElementString() {
var s = new StringBuilder();
var median = this.GSystem.Order / 2;
for (byte e = 0; e < median; e += 1) { //// 2016/04 was e += 2
if ((e * 2) + 1 >= this.ElementList.Count) {
continue;
}
var elem1 = (byte)this.ElementList[e * 2];
var elem2 = (byte)this.ElementList[(e * 2) + 1];
if (elem2 == 0) {
switch (elem1) {
case 0:
s.Append("-");
break;
case 1:
s.Append("T");
break;
case 2:
s.Append("P");
break;
//// resharper default: break;
}
}
else {
s.Append("X");
}
}
return s.ToString();
}
/// Short string representation of the object.
/// Returns value.
public string ToShortString() {
var s = new StringBuilder();
s.Append(" " + this.ElementString());
//// s.Append(" " + this.DistanceString());
return s.ToString();
}
/// String representation of the object.
/// Returns value.
public override string ToString() {
var s = new StringBuilder();
s.Append(string.Format(CultureInfo.InvariantCulture, "{0},{1}", base.ToString(), this.ElementString()));
s.Append(",");
s.AppendLine(this.StringOfProperties());
return s.ToString();
}
#endregion
#region Relation to previous struct
/// Sets previous harmonic structure and Compute corresponding properties.
/// Harmonic structure.
public void SetPreviousStruct(RhythmicStructure structure) {
Contract.Requires(structure != null);
this.PreviousStruct = structure;
this.DetermineBehaviorFromPreviousStruct();
}
#endregion
#region Private static methods
///
/// Adds the partial elements.
///
/// The rhythmic structure.
/// The CNT.
/// The last value.
private static void AddPartialElements(RhythmicStructure rs, byte cnt, byte lastValue) {
Contract.Requires(rs != null);
var d = (byte)(cnt / 2);
rs.ElementList.Add(lastValue);
for (byte n = 0; n < d; n++) {
rs.ElementList.Add(0);
}
if (lastValue == 1 && cnt % 2 == 0) {
rs.ElementList.Add(lastValue);
}
for (byte n = 0; n < d; n++) {
rs.ElementList.Add(0);
}
}
#endregion
#region Private methods
///
/// Determine and sets the mobility property.
///
/// Returns value.
private float ComputeToneMobility() {
//// float mobility = this.GSystem.Order > 0 ? (this.Level / (float)this.GSystem.Order) * 100f : 0;
//// float mobility = this.GSystem.Order > 0 ? (this.ToneLevel / (float)this.GSystem.Order) * 100f : 0;
var toneMobility = (this.ToneLevel / (float)this.Level) * 100f;
return toneMobility;
}
/// Sets properties of the structure with regard to previous structure.
private void DetermineBehaviorFromPreviousStruct() {
Contract.Requires(this.PreviousStruct != null);
//// var rhythmicSystem = (RhythmicSystem)this.GSystem;
//// var harRelation = new HarmonicRelation(harmonicSystem, this.PreviousStruct, this);
//// var continuity = harRelation.MeanValueOfProperty(GenProperty.FormalContinuity, false, true);
//// var impulse = harRelation.MeanValueOfProperty(GenProperty.FormalImpulse, false, true);
//// this.Properties[GenProperty.RelatedContinuity] = continuity; // add with repetition
//// this.Properties[GenProperty.RelatedImpulse] = impulse; // add with repetition
}
#endregion
}
}